package com.wjf.birdfish.commons;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.wjf.birdfish.util.BeanUtils;
import com.wjf.birdfish.util.Page;
import com.wjf.birdfish.util.PageSearchEntity;


/**
 * Created on 2006-10-28
 * <p>
 * Title: Hibernate Dao的泛型基类.
 * </p>
 * <p>
 * Description:继承于Spring的HibernateDaoSupport， 提供分页函数和若干便捷查询方法， 并对返回值作了泛型类型转换.
 * </p>
 * <p>
 * Copyright: Copyright TYDIC (c) 2006
 * </p>
 * <p>
 * Company: tydic.com
 * </p>
 * 
 * @author <a href="mailto:tongys@tydic.com">童永胜</a>
 * @see HibernateDaoSupport
 * @version 1.0
 */
@SuppressWarnings("unchecked")
public class HibernateGenericDao extends HibernateDaoSupport {

	org.springframework.orm.hibernate3.HibernateTemplate hibernateTemplateFile;

	/**
	 * 根据ID获取对象
	 */
	public <T> T get(Class<T> entityClass, Serializable id) {
		getHibernateTemplate().getSessionFactory().getCurrentSession()
				.setFlushMode(FlushMode.AUTO);
		T o = (T) getHibernateTemplate().get(entityClass, id);
		if (o == null)
			throw new ObjectRetrievalFailureException(entityClass, id);
		getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
		return o;
	}

	/**
	 * 获取全部对象
	 */
	public <T> List<T> getAll(Class<T> entityClass, boolean useState) {
		if (useState) {
			return getHibernateTemplate().find(
					"from " + entityClass.getName() + " where state='1'");
		} else {
			return getHibernateTemplate().find("from " + entityClass.getName());
		}
	}

	public <T> List<T> getAll(Class<T> entityClass, String orgId,
			boolean useState) {
		String hql = "from " + entityClass.getName() + " where orgId=?";
		if (useState)
			hql += " and state='1'";
		return getHibernateTemplate().find(hql, new Object[] { orgId });
	}

	/**
	 * 保存对象
	 */
	public void save(Object o) {
		getHibernateTemplate().getSessionFactory().getCurrentSession()
				.setFlushMode(FlushMode.AUTO);
		getHibernateTemplate().save(o);
		getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
	}

	/**
	 * 更新对象
	 */
	public void update(Object o) {
		getHibernateTemplate().getSessionFactory().getCurrentSession()
				.setFlushMode(FlushMode.AUTO);
		getHibernateTemplate().update(o);
		getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
	}

	public void marge(Object o) {
		getHibernateTemplate().getSessionFactory().getCurrentSession()
				.setFlushMode(FlushMode.AUTO);
		getHibernateTemplate().merge(o);
		getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
	}

	/**
	 * 删除对象
	 */
	public void delete(Object o) {
		getHibernateTemplate().getSessionFactory().getCurrentSession()
				.setFlushMode(FlushMode.AUTO);
		getHibernateTemplate().delete(o);
		getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
	}

	/**
	 * 删除多个对象
	 * 
	 * @param o
	 *            包装了要删除对象的集合类
	 */
	public void deleteAll(Collection o) {
		getHibernateTemplate().getSessionFactory().getCurrentSession()
				.setFlushMode(FlushMode.AUTO);
		getHibernateTemplate().deleteAll(o);
		getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
	}

	/**
	 * hql查询.
	 * 
	 * @param values
	 *            可变参数 用户可以如下四种方式使用 dao.find(hql) dao.find(hql,arg0);
	 *            dao.find(hql,arg0,arg1); dao.find(hql,new
	 *            Object[arg0,arg1,arg2])
	 */
	public List find(String hql, Object... values) {
		if (values.length == 0)
			return getHibernateTemplate().find(hql);
		else
			return getHibernateTemplate().find(hql, values);
	}

	/**
	 * 根据属性名和属性值查询对象.
	 * 
	 * @return 符合条件的对象列表
	 */
	public <T> List<T> findBy(Class<T> entityClass, String name, Object value) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.eq(name, value));
		return criteria.list();
	}

	public <T> List<T> findBy(Class<T> entityClass, String name, Object value,
			String orgId) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.eq(name, value));
		criteria.add(Restrictions.eq("orgId", orgId));
		return criteria.list();
	}

	/**
	 * 根据属性名和属性值查询对象.
	 * 
	 * @return 符合条件的唯一对象
	 */
	public <T> T findUniqueBy(Class<T> entityClass, String name, Object value) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.eq(name, value));
		return (T) criteria.uniqueResult();
	}

	public <T> T findUniqueBy(Class<T> entityClass, String name, Object value,
			String orgId) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.eq(name, value));
		criteria.add(Restrictions.eq("orgId", orgId));
		return (T) criteria.uniqueResult();
	}

	/**
	 * 根据属性名和属性值查询对象. 这个实现方法没有使用Criteria查询对象
	 * 
	 * @return 符合条件的唯一对象
	 */
	public <T> T findUniqueBy2(Class<T> entityClass, String name, Object value) {
		List<T> list = null;
		String hql = "from " + entityClass.getName() + " where " + name + "=?";
		list = find(hql, value);
		if (!list.isEmpty()) {
			return (T) list.get(0);
		} else {
			return null;
		}
	}

	/**
	 * 根据属性名和属性值以Like AnyWhere方式查询对象.
	 */
	public <T> List<T> findByLike(Class<T> entityClass, String name,
			String value) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.like(name, value, MatchMode.ANYWHERE));
		return criteria.list();
	}

	/**
	 * 取得Entity的Criteria.
	 */
	protected <T> Criteria getEntityCriteria(Class<T> entityClass) {
		return getSession().createCriteria(entityClass);
	}

	/**
	 * 判断对象某些属性的值在数据库中不存在重复
	 * 
	 * @param names
	 *            在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 */
	public <T> boolean isNotUnique(Class<T> entityClass, Object entity,
			String names) {
		Assert.hasText(names);
		Criteria criteria = getEntityCriteria(entityClass).setProjection(
				Projections.rowCount());
		String[] nameList = names.split(",");
		try {
			// 循环加入
			for (String name : nameList) {
				criteria.add(Restrictions.eq(name,
						PropertyUtils.getProperty(entity, name)));
			}

			// 以下代码为了如果是update的情况,排除entity自身.

			// 通过Hibernate的MetaData接口取得主键名
			String idPropertyName = getSessionFactory().getClassMetadata(
					entity.getClass()).getIdentifierPropertyName();
			if (idPropertyName != null) {
				// 通过反射取得entity的主键值
				Object id = PropertyUtils.getProperty(entity, idPropertyName);
				// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
				if (id != null)
					criteria.add(Restrictions.not(Restrictions.eq(
							idPropertyName, id)));
			}

		} catch (IllegalAccessException e) {
			logger.error("Error when reflection on entity," + e.getMessage());
			return false;
		} catch (InvocationTargetException e) {
			logger.error("Error when reflection on entity," + e.getMessage());
			return false;
		} catch (NoSuchMethodException e) {
			logger.error("Error when reflection on entity," + e.getMessage());
			return false;
		}
		return (Integer) criteria.uniqueResult() > 0;
	}

	/**
	 * 分页查询函数，使用Criteria.
	 * 
	 * @param pageNo
	 *            页号,从0开始.
	 */
	public Page pagedQuery(Criteria criteria, int pageNo, int pageSize) {
		CriteriaImpl impl = (CriteriaImpl) criteria;

		// 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
		Projection projection = impl.getProjection();
		List<CriteriaImpl.OrderEntry> orderEntries;
		try {
			orderEntries = (List) BeanUtils.getPrivateProperty(impl,
					"orderEntries");
			BeanUtils.setPrivateProperty(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}

		// 执行查询
		long totalCount = (Long) criteria.setProjection(Projections.rowCount())
				.uniqueResult();

		// 将之前的Projection和OrderBy条件重新设回去
		criteria.setProjection(projection);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.setPrivateProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}

		// 返回分页对象
		if (totalCount < 1)
			return new Page();

		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		List list = criteria.setFirstResult(startIndex).setMaxResults(pageSize)
				.list();

		return new Page(startIndex, totalCount, pageSize, list);
	}

	/**
	 * 分页查询函数，使用hql.
	 * 
	 * @param pageNo
	 *            页号,从0开始.
	 */
	public Page pagedQuery(String hql, int pageNo, int pageSize, Object... args) {
		Assert.hasText(hql);
		// 创建查询
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < args.length; i++) {
			query.setParameter(i, args[i]);
		}
		String countQueryString = " select count (*) "
				+ removeSelect(removeOrders(hql));
		List countlist = getHibernateTemplate().find(countQueryString, args);
		long totalCount = countlist.isEmpty()? 0:(Long) countlist.get(0);

		// 返回分页对象
		if (totalCount < 1)
			return new Page();
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		List list = query.setFirstResult(startIndex).setMaxResults(pageSize)
				.list();

		// getSession().close();

		return new Page(startIndex, totalCount, pageSize, list);
	}

	/**
	 * 去除hql的select 子句，未考虑union的情况,，用于pagedQuery.
	 */
	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int beginPos = hql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, " hql : " + hql
				+ " must has a keyword 'from'");
		return hql.substring(beginPos);
	}

	/**
	 * 去除hql的orderby 子句，用于pagedQuery.
	 */
	private static String removeOrders(String hql) {
		Assert.hasText(hql);
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 根据查询实体进行分页查询
	 * 
	 * @param entityClass
	 * @param entity
	 * @param pageNo
	 * @param pageSize
	 * @return
	 * @throws Exception
	 */
	public <T> Page findByEntity(Class<T> entityClass, PageSearchEntity entity,
			int pageNo, int pageSize) {
		StringBuffer hql = new StringBuffer();
		hql.append("from " + entityClass.getName());
		Object objs[] = new Object[] {};
		if (entity != null) {
			List ls = entity.getParaKey();
			int i = 0;
			for (i = 0; i < ls.size(); i++) {
				if (i == 0)
					hql.append(" where ");
				else
					hql.append(entity.getCondList().get(i));
				hql.append(ls.get(i));
			}

			if (StringUtils.isNotBlank(entity.getSql()))
				hql.append(" " + entity.getSql() + " ");
			if (StringUtils.isNotBlank(entity.getOrderSql())) {
				hql.append(" order by " + entity.getOrderSql());
			}

			objs = entity.getParaValue().toArray();
		}
		return pagedQuery(hql.toString(), pageNo, pageSize, objs);
	}

	public <T> Page findByEntity2(Class<T> entityClass,
			PageSearchEntity entity, int pageNo, int pageSize) {
		StringBuffer hql = new StringBuffer();

		hql.append("select orderId ,orderType, orderContent,orderTime,planFinishTime,orderStatus,orderOperator,orderResult,orderMemo,orderSubmitor,orderNo from "
				+ entityClass.getName());
		// hql.append("select orderId ,orderType, orderContent,to_date(orderTime,'YYYY-MM-DD HH:MI:SS'),to_date( planFinishTime,'YYYY-MM-DD HH:MI:SS'),orderStatus,orderOperator,orderResult,orderMemo,orderSubmitor,orderNo from "
		// + entityClass.getName());
		Object objs[] = new Object[] {};
		if (entity != null) {
			List ls = entity.getParaKey();
			int i = 0;
			for (i = 0; i < ls.size(); i++) {
				if (i == 0)
					hql.append(" where ");
				else
					hql.append(entity.getCondList().get(i));
				hql.append(ls.get(i));
			}

			if (StringUtils.isNotBlank(entity.getSql()))
				hql.append(" " + entity.getSql() + " ");
			if (StringUtils.isNotBlank(entity.getOrderSql())) {
				hql.append(" order by " + entity.getOrderSql());
			}

			objs = entity.getParaValue().toArray();
		}
		return pagedQuery(hql.toString(), pageNo, pageSize, objs);
	}

	public Page findByHql(String hql, int pageNo, int pageSize, Object... objs) {
		return pagedQuery(hql.toString(), pageNo, pageSize, objs);
	}

	/**
	 * 根据排序字段找到下一个排序号
	 * 
	 * @author tongys
	 * @param <T>
	 * @param entityClass
	 * @param orderName
	 * @return
	 */
	public <T> int getInitOrder(Class<T> entityClass, String orderName) {
		List list = null;
		String hql = "select Max(" + orderName + ") from "
				+ entityClass.getName();
		list = find(hql);
		int order = 1;
		if (list != null && list.get(0) != null) {
			order = (Integer) list.get(0) + 1;
		}
		return order;
	}

	/**
	 * 根据排序字段找到下一个排序号(数据库中序号类型为varchar)
	 * 
	 * @author tongys
	 * @param <T>
	 * @param entityClass
	 * @param orderName
	 * @return
	 */
	public <T> int getInitOrder2(Class<T> entityClass, String orderName) {
		List list = null;
		String hql = "select Max(" + orderName + ") from "
				+ entityClass.getName();
		list = find(hql);
		String index = "1";
		if (list != null && list.get(0) != null) {
			index = (String) list.get(0);
		}
		return Integer.parseInt(index);
	}

	/**
	 * @author tongys 根据参数得到List
	 * @throws Exception
	 */
	public <T> List findListByEntity(Class<T> entityClass,
			PageSearchEntity entity) {
		StringBuffer hql = new StringBuffer();
		hql.append("from " + entityClass.getName());
		Object objs[] = new Object[] {};
		if (entity != null) {
			List ls = entity.getParaKey();

			for (int i = 0; i < ls.size(); i++) {
				if (i == 0)
					hql.append(" where ");
				else
					hql.append(" and ");
				hql.append(ls.get(i));
			}
			if (StringUtils.isNotBlank(entity.getOrderSql())) {
				hql.append(" order by " + entity.getOrderSql());
			}
			objs = entity.getParaValue().toArray();
		}

		List list = getHibernateTemplate().find(hql.toString(), objs);
		return list;
	}

	/**
	 * 附件保存dao
	 * 
	 * @author tongys
	 * @param o
	 */
	public void saveFile(Object o) {
		hibernateTemplateFile.save(o);
	}

	/**
	 * 删除附件dao
	 * 
	 * @author tongys
	 * @param o
	 */
	public void deleteFile(Object o) {
		hibernateTemplateFile.delete(o);
	}

	public org.springframework.orm.hibernate3.HibernateTemplate getHibernateTemplateFile() {
		return hibernateTemplateFile;
	}

	public void setHibernateTemplateFile(
			org.springframework.orm.hibernate3.HibernateTemplate hibernateTemplateFile) {
		this.hibernateTemplateFile = hibernateTemplateFile;
	}
}